Page({
  data: {
    // 实时检测到的条形码框（CSS 像素坐标）
    barcodeBoxes: [],
    uploading: false,
  },

  onReady() {
    // 初始化屏幕尺寸，用于将帧坐标映射到视图坐标
    const sys = wx.getSystemInfoSync();
    this._viewportWidth = sys.windowWidth;
    this._viewportHeight = sys.windowHeight;

    this._cameraCtx = wx.createCameraContext();
    this._smoothedBox = null;
    this._lastProcessTs = 0;
    this._startRealtimeDetect();
  },

  onUnload() {
    this._stopRealtimeDetect();
  },

  onTakeAndUpload() {
    if (this.data.uploading) return;
    const ctx = wx.createCameraContext();
    this.setData({ uploading: true });
    ctx.takePhoto({
      quality: 'low',
      success: async (res) => {
        try {
          const path = res.tempImagePath;
          const base64 = await this._readAsBase64(path);
          // 可选：上传照片（与本地实时检测互不影响）
          await this._uploadBase64(base64);
          wx.showToast({ title: '已上传', icon: 'success' });
        } catch (e) {
          wx.showToast({ title: '上传失败', icon: 'none' });
        } finally {
          this.setData({ uploading: false });
        }
      },
      fail: () => {
        this.setData({ uploading: false });
      }
    });
  },

  _readAsBase64(filePath) {
    const fs = wx.getFileSystemManager();
    return new Promise((resolve, reject) => {
      fs.readFile({
        filePath,
        encoding: 'base64',
        success: (res) => resolve(res.data),
        fail: reject,
      });
    });
  },

  _uploadBase64(base64) {
    return new Promise((resolve, reject) => {
      wx.request({
        url: 'https://example.com/upload',
        method: 'POST',
        header: { 'Content-Type': 'application/json' },
        data: { image: base64 },
        success: (r) => {
          if (r.statusCode >= 200 && r.statusCode < 300) resolve(r);
          else reject(new Error('bad status ' + r.statusCode));
        },
        fail: reject,
      });
    });
  },

  _startRealtimeDetect() {
    if (!this._cameraCtx || !this._cameraCtx.onCameraFrame) return;
    if (this._frameListener) return;
    const listener = this._cameraCtx.onCameraFrame((frame) => {
      const now = Date.now();
      // 节流：每 ~100ms 处理一次，避免对性能影响过大
      if (now - this._lastProcessTs < 100) return;
      this._lastProcessTs = now;
      try {
        const candidateBox = this._processFrameForBarcodeBox(frame);
        if (candidateBox) {
          const smooth = this._smoothBox(candidateBox);
          const cssBox = this._mapToCssBox(smooth, frame.width, frame.height);
          this.setData({ barcodeBoxes: [cssBox] });
        } else {
          // 未检测到时，清空或保留上次结果，这里选择清空
          this.setData({ barcodeBoxes: [] });
          this._smoothedBox = null;
        }
      } catch (e) {
        // 忽略单帧异常
      }
    });
    listener.start();
    this._frameListener = listener;
  },

  _stopRealtimeDetect() {
    if (this._frameListener && this._frameListener.stop) {
      try { this._frameListener.stop(); } catch (e) {}
    }
    this._frameListener = null;
  },

  // 从一帧 RGBA 数据中粗略估计条形码位置，返回帧坐标系下的 {x,y,w,h}
  _processFrameForBarcodeBox(frame) {
    const { data, width, height } = frame;
    if (!data || !width || !height) return null;

    // 降采样参数（取中间 60% 高度区域，提高稳定性）
    const yStart = Math.floor(height * 0.2);
    const yEnd = Math.floor(height * 0.8);
    const rowStep = 4;   // 每 4 行取一行
    const colStep = 2;   // 每 2 列取一列

    const rowStride = width * 4;
    const columnScore = new Float32Array(width);

    // 计算每一列的横向灰度变化累计值（条码会在水平方向有明显的黑白交替）
    for (let y = yStart; y < yEnd; y += rowStep) {
      let base = y * rowStride;
      // 前一个像素灰度
      let prevGray = 0;
      // 初始化 prevGray
      {
        const r = data[base];
        const g = data[base + 1];
        const b = data[base + 2];
        prevGray = (r * 299 + g * 587 + b * 114) / 1000;
      }
      for (let x = 1; x < width; x += colStep) {
        const idx = base + x * 4;
        const r = data[idx];
        const g = data[idx + 1];
        const b = data[idx + 2];
        const gray = (r * 299 + g * 587 + b * 114) / 1000;
        const diff = Math.abs(gray - prevGray);
        columnScore[x] += diff;
        prevGray = gray;
      }
    }

    // 平滑列得分
    const smoothWin = 9;
    const smoothed = new Float32Array(width);
    for (let x = 0; x < width; x++) {
      let sum = 0, cnt = 0;
      for (let k = -Math.floor(smoothWin / 2); k <= Math.floor(smoothWin / 2); k++) {
        const ix = x + k;
        if (ix >= 0 && ix < width) { sum += columnScore[ix]; cnt++; }
      }
      smoothed[x] = cnt ? sum / cnt : 0;
    }

    // 根据阈值找到最长连续高分区间，作为条码的横向范围
    let maxVal = 0;
    for (let x = 0; x < width; x++) if (smoothed[x] > maxVal) maxVal = smoothed[x];
    if (maxVal <= 0) return null;
    const threshold = maxVal * 0.6;
    let bestStart = -1, bestEnd = -1, curStart = -1;
    for (let x = 0; x < width; x++) {
      if (smoothed[x] >= threshold) {
        if (curStart === -1) curStart = x;
      } else if (curStart !== -1) {
        if (bestStart === -1 || x - 1 - curStart > bestEnd - bestStart) {
          bestStart = curStart; bestEnd = x - 1;
        }
        curStart = -1;
      }
    }
    if (curStart !== -1) {
      if (bestStart === -1 || width - 1 - curStart > bestEnd - bestStart) {
        bestStart = curStart; bestEnd = width - 1;
      }
    }
    if (bestStart === -1) return null;

    const x0 = Math.max(0, bestStart - 4);
    const x1 = Math.min(width - 1, bestEnd + 4);

    // 再估计纵向范围：在 [x0, x1] 内，统计每一行的横向变化强度
    const rowScore = new Float32Array(height);
    for (let y = yStart; y < yEnd; y += rowStep) {
      let base = y * rowStride + x0 * 4;
      let prevGray = 0;
      {
        const r = data[base];
        const g = data[base + 1];
        const b = data[base + 2];
        prevGray = (r * 299 + g * 587 + b * 114) / 1000;
      }
      for (let x = x0 + 1; x <= x1; x += colStep) {
        const idx = y * rowStride + x * 4;
        const r = data[idx];
        const g = data[idx + 1];
        const b = data[idx + 2];
        const gray = (r * 299 + g * 587 + b * 114) / 1000;
        rowScore[y] += Math.abs(gray - prevGray);
        prevGray = gray;
      }
    }
    // 平滑行得分并确定纵向范围
    let maxRow = 0;
    for (let y = 0; y < height; y++) if (rowScore[y] > maxRow) maxRow = rowScore[y];
    if (maxRow <= 0) return null;
    const rowThreshold = maxRow * 0.5;
    let bestY0 = -1, bestY1 = -1, curY0 = -1;
    for (let y = 0; y < height; y++) {
      if (rowScore[y] >= rowThreshold) {
        if (curY0 === -1) curY0 = y;
      } else if (curY0 !== -1) {
        if (bestY0 === -1 || y - 1 - curY0 > bestY1 - bestY0) {
          bestY0 = curY0; bestY1 = y - 1;
        }
        curY0 = -1;
      }
    }
    if (curY0 !== -1) {
      if (bestY0 === -1 || height - 1 - curY0 > bestY1 - bestY0) {
        bestY0 = curY0; bestY1 = height - 1;
      }
    }
    if (bestY0 === -1) return null;

    // 适当扩展边界，避免裁切过紧
    const expandX = Math.floor((x1 - x0) * 0.1);
    const expandY = Math.floor((bestY1 - bestY0) * 0.2);
    const bx0 = Math.max(0, x0 - expandX);
    const bx1 = Math.min(width - 1, x1 + expandX);
    const by0 = Math.max(0, bestY0 - expandY);
    const by1 = Math.min(height - 1, bestY1 + expandY);

    const box = { x: bx0, y: by0, w: Math.max(1, bx1 - bx0 + 1), h: Math.max(1, by1 - by0 + 1) };
    // 过滤过小框
    if (box.w < width * 0.05 || box.h < height * 0.05) return null;
    return box;
  },

  _smoothBox(box) {
    if (!this._smoothedBox) { this._smoothedBox = box; return box; }
    const alpha = 0.5; // 平滑系数
    const s = this._smoothedBox;
    const result = {
      x: s.x * (1 - alpha) + box.x * alpha,
      y: s.y * (1 - alpha) + box.y * alpha,
      w: s.w * (1 - alpha) + box.w * alpha,
      h: s.h * (1 - alpha) + box.h * alpha,
    };
    this._smoothedBox = result;
    return result;
  },

  _mapToCssBox(box, frameW, frameH) {
    const vw = this._viewportWidth || frameW;
    const vh = this._viewportHeight || frameH;
    return {
      x: (box.x / frameW) * vw,
      y: (box.y / frameH) * vh,
      w: (box.w / frameW) * vw,
      h: (box.h / frameH) * vh,
    };
  },
});


